package br.org.acessobrasil.processoacessibilidade.bo;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import br.org.acessobrasil.ases.ferramentas_de_reparo.controle.XHTMLControle;
import br.org.acessobrasil.ases.regras.RegrasHardCodedEmag;

/**
 * Faz ajustes comuns, de forma automatica
 * @author Fabio Issamu Oshiro, Jonatas Pacheco Ribeiro
 */
public class Retificador {
	private static Logger logger = Logger.getLogger(Retificador.class);
	private static Pattern patDocType = Pattern.compile("\\s*<!DOCTYPE\\s[^>]*>");
	private static Pattern patTargetSelf = Pattern.compile("<[^>]*target=([\"']_self[\"']|_self)[^>]*>",Pattern.CASE_INSENSITIVE);
	private static Pattern patTargetBlank = Pattern.compile("<a\\s[^>]*target=([\"']_blank[\"']|_blank|\"blank\")[^>]*>",Pattern.CASE_INSENSITIVE);
	private static Pattern patLinkAdjacente = Pattern.compile("(</a\\s*>)(<a\\s[^>]*>)",Pattern.CASE_INSENSITIVE);
	private static Pattern patLinkAdjacenteIgual = Pattern.compile("(<a\\s[^>]*>)(.*?)(</a\\s*>)",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static Pattern patScript = Pattern.compile("<script\\s[^>]*>",Pattern.CASE_INSENSITIVE);
	private static Pattern patHtml = Pattern.compile("<html[^>]*>",Pattern.CASE_INSENSITIVE);
	private static Pattern patObject = Pattern.compile("(<object\\s[^>]*>)(.*?)(</object>)",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	private static Pattern patImg = Pattern.compile("(<img\\s[^>]*>)",Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	
	private final static String DOCTYPE_PADRAO = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";	
	private final static String SUBSTITUTO_TARGETBLANK="onclick=\"window.open(this.href);return false;\"";
	private final static String AVISO_NOVAJANELA="Abrir&aacute; em uma nova janela.";

	private final static String scriptTypes[]={
			"text/ecmascript",
			"text/javascript",
			"application/ecmascript",
			"application/javascript",
			"text/vbscript"
	};
	
	
	private final static String TAGHTML_PADRAO = "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"pt-br\" lang=\"pt-br\">";
	
	private static RegrasHardCodedEmag regras = new RegrasHardCodedEmag();
	private boolean colocarDocType;
	
	public String consertarErroHtml(String codHtml) {
		if(codHtml==null){
			logger.warn("codHtml = null", new RuntimeException());
			return null;
		}
		codHtml = arrumarScript(codHtml);
		codHtml = retirarTargetSelf(codHtml);
		codHtml = arrumarFlash(codHtml);
		codHtml = fecharTag(codHtml);
		codHtml = arrumarTargetBlank(codHtml);
		codHtml = colocarAltVazio(codHtml);
		codHtml = codHtml.replace("<EM>","<em>");
		codHtml = codHtml.replace("</EM>","</em>");
		codHtml = codHtml.replace("<HEAD>","<head>");
		codHtml = codHtml.replace("</HEAD>","</head>");
		codHtml = codHtml.replace("<TITLE>","<title>");
		codHtml = codHtml.replace("</TITLE>","</title>");
		return codHtml;
	}
	/**
	 * coloca alt vazio quando nao existe o atributo.<br>
	 * Eg.: alt=""
	 * @param codHtml codigo alterado
	 * @return todas as tags img com atributo alt vazio
	 */
	private String colocarAltVazio(String codHtml) {
		Matcher mat = patImg.matcher(codHtml);
		while(mat.find()){
			String tag = mat.group();
			String novaTag = tag;
			String alt = regras.getAtributo(tag, "alt");
			if(alt.equals("")){
				novaTag = novaTag.replaceAll("\\salt=\"\"","")
					.replaceAll("\\salt=''","");
				novaTag = novaTag.substring(0,4) + " alt=\"\""+novaTag.substring(4);
				codHtml = codHtml.replace(tag, novaTag);
			}
		}
		return codHtml;
	}
	/**
	 * Concatena o title antigo
	 * @param codHtml
	 * @return codHtml com os target blank substituidos
	 */
	public static String arrumarTargetBlank(String codHtml){
		//TODO fazer para qualquer target
		
		Matcher mat = patTargetBlank.matcher(codHtml);
		
		while(mat.find()){
			int ini = mat.start();
			int fim = mat.end();
			String title = regras.getAtributo(mat.group(), "title");
			String newTitle = title;
			if(title==null || title.equals("")){
				newTitle = AVISO_NOVAJANELA;
			}else{
				if(title.trim().endsWith(".")){
					newTitle+=" " + AVISO_NOVAJANELA;
				}else{
					newTitle+=". " + AVISO_NOVAJANELA;
				}
			}
			String tag = mat.group();
			if(title==null || title.equals("")){
				if(tag.endsWith("/>")){
					tag = tag.substring(0,tag.length()-2);
					tag+=" title=\""+newTitle+"\" />";
				}else if(tag.endsWith(">")){
					tag = tag.substring(0,tag.length()-1);
					tag+=" title=\""+newTitle+"\" >";
				}
			}else{
				tag = tag.replace("title=\""+title+"\"","title=\""+newTitle+"\"")
				.replace("title='"+title+"'","title=\""+newTitle+"\"")
				.replaceAll("title="+title,"title=\""+newTitle+"\"");
				
			}
			String codFim = codHtml.substring(fim);
			codHtml = codHtml.substring(0,ini);
			
			codHtml += tag
				.replaceAll("target=[\"']_blank[\"']",SUBSTITUTO_TARGETBLANK)
				.replaceAll("target=_blank",SUBSTITUTO_TARGETBLANK)	
				.replaceAll("target=\"blank\"",SUBSTITUTO_TARGETBLANK)
				+codFim;				
			mat = patTargetBlank.matcher(codHtml);
		}
		return codHtml;
	}
	/**
	 * 
	 * @param codHtml
	 * @return codigo sem target _self
	 */
	public static String retirarTargetSelf(String codHtml){
		Matcher mat = patTargetSelf.matcher(codHtml);
		while(mat.find()){
			int ini = mat.start();
			int fim = mat.end();
			
			String codFim = codHtml.substring(fim);
			codHtml = codHtml.substring(0,ini);
			codHtml += mat.group()
				.replaceAll("target=[\"']_self[\"']","")
				.replaceAll("target=_self","")	
				+codFim;				
			mat = patTargetSelf.matcher(codHtml);
		}
		return codHtml;
	}
	/**
	 * 
	 * @param codHtml codigo de trabalho
	 * @return codHtml Alterado, com correcoes previas
	 */
	public String consertarErroAcessibilidade(String codHtml) {
		if(codHtml == null){
			logger.warn("consertarErroAcessibilidade(codHtml = null)!");
			return null;
		}
		if(colocarDocType){
			//ver docType
			logger.debug("Arrumando DocType");
			codHtml = arrumarDocType(codHtml);
		}
		//ver idioma
		logger.debug("Arrumando Idioma");
		codHtml = arrumarIdioma(codHtml);
		logger.debug("Arrumando LinksAdjacentes");
		codHtml = arrumarLinksAdjacentes(codHtml);
		logger.debug("retornando resultado");
		return codHtml;
	}
	
	public static boolean isScriptTypeValid(String type){
		for(int i=0;i<scriptTypes.length;i++){
			if(type.equals(scriptTypes[i])){
				return true;
			}
		}
		return false;
	}
	
	public static String arrumarLinksAdjacentes(String codHtml){
		{
			//Arrumar os adjacentes e iguais
			Matcher mat = patLinkAdjacenteIgual.matcher(codHtml);
			String linkAnt = "";
			
			int fimAnt=0;
			int lenCloseA = 0;
			while(mat.find()){
				//System.out.println("'"+mat.group()+"'");
				int ini = mat.start();
				int fim = mat.end();
				String linkAtual = mat.group(1);
				String tagFecha = mat.group(3); 
				if(linkAnt.equals(linkAtual)){
					//verificar se o anterior termina exatamente antes deste
					if(fimAnt==ini){
						//retirar o cod 
						codHtml=codHtml.substring(0,ini-lenCloseA)+codHtml.substring(ini+linkAtual.length());
						mat = patLinkAdjacenteIgual.matcher(codHtml);
					}
				}
				lenCloseA = tagFecha.length();
				linkAnt = linkAtual;
				fimAnt = fim;
			}
		}
		{
			//Arrumar os adjacentes e nao iguais
			Matcher mat = patLinkAdjacente.matcher(codHtml);
			while(mat.find()){
				int ini = mat.start();
				int fim = mat.end();
				codHtml = codHtml.substring(0,ini)+mat.group(1)+" "+mat.group(2)+codHtml.substring(fim);
				//System.out.println("..");
				mat = patLinkAdjacente.matcher(codHtml);
			}
		}
		return codHtml;
	}
	
	public static String arrumarScript(String codHtml){
		String retorno=codHtml;
		Matcher mat = patScript.matcher(codHtml);
		while(mat.find()){
			String src = regras.getAtributo(mat.group(),"src");
			String type = regras.getAtributo(mat.group(),"type").toLowerCase();
			String charset = regras.getAtributo(mat.group(),"charset");
			String defer = regras.getAtributo(mat.group(),"defer");
			if(!isScriptTypeValid(type)){
				type ="text/javascript";
			}
			String newTag = "<script type=\""+type+"\"";
			if(src!=null && !src.equals("")){
				newTag+=" src=\""+src+"\"";
			}
			if(charset!=null && !charset.equals("")){
				newTag+=" charset=\""+charset+"\"";
			}
			if(defer!=null && !defer.equals("")){
				newTag+=" defer=\""+defer+"\"";
			}
			newTag+=">";
			retorno = retorno.replace(mat.group(),newTag);
		}
		return retorno.replace("</SCRIPT>", "</script>");
	}
	
	/**
	 * 
	 * @param codHtml
	 * @return codigo com o doctype padrao
	 */
	private String arrumarDocType(String codHtml){
		Matcher mat = patDocType.matcher(codHtml);
		if(!mat.find()){
			codHtml=DOCTYPE_PADRAO+"\n"+codHtml;
		}else{
			codHtml=mat.replaceFirst(DOCTYPE_PADRAO);
		}
		return codHtml;
	}
	/**
	 * Nao identifica o idioma, somente coloca a tag html padrao<br>
	 * O ideal seria fazer uma estatistica de quantas palavras existem em cada idioma e
	 * escolher o que possui mais
	 * @param codHtml
	 * @return codHtml alterado com a tag html padrao
	 */
	private String arrumarIdioma(String codHtml){
		Matcher mat = patHtml.matcher(codHtml);
		if(mat.find()){
			codHtml = mat.replaceAll(TAGHTML_PADRAO);
		}
		return codHtml;
	}
	/**
	 * Ainda em observacao
	 * @param codHtml
	 * @return codigo com tags fechadas
	 */
	public static String fecharTag(String codHtml){
		XHTMLControle xhtmlControle = new XHTMLControle();
		try{
			codHtml = xhtmlControle.fecharTags(codHtml);
		}catch(NoSuchMethodError e){
			logger.error("Metodo xhtmlControle.fecharTags(codHtml) nao encontrado");
		}catch(Exception e){
			e.printStackTrace();
		}
		return codHtml;
	}
	/**
	 * &lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" width="500" height="400" title="Abertura-Nise da Silveira-Vida e Obra">
        	&lt;param name="movie" value="arquivos/swf/95_homepage_FINAL.swf" />
        	&lt;param name="quality" value="high" />
        	&lt;embed src="arquivos/swf/95_homepage_FINAL.swf" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="500" height="400"></embed>
      	&lt;/object>
	 * @param codHtml
	 * @return
	 */
	public static String arrumarFlash(String codHtml){
		/*
		 * 
		 <object type="application/x-shockwave-flash" data="arquivos/swf/flash3.swf"  width="100%" height="100%" id="VideoPlayback" >
						<param name="movie" value="arquivos/swf/flash3.swf"  />
						<param name="allowScriptAcess" value="sameDomain" />
						<param name="quality" value="best" />
						<param name="bgcolor" value="#FFFFFF" />
						<param name="salign" value="TC" />
						<param name="wmode" value="transparent" />
						<param name="FlashVars" value="playerMode=embedded" />
						Fotos da Biblioteca Nacional
					</object>

		 */
		String retorno = codHtml;
		Matcher mat = patObject.matcher(codHtml);
		while(mat.find()){
			String tagObject = mat.group();
			//propriedades do object
			String id,title,width,height,archive,classid,codebase,codetype,data,declare,name,standby,type,usemap,vspace;
			title = regras.getAtributo(tagObject, "title");
			id = regras.getAtributo(tagObject, "id");
			width = regras.getAtributo(tagObject, "width");
			height = regras.getAtributo(tagObject, "height");
			archive = regras.getAtributo(tagObject, "archive");
			classid = regras.getAtributo(tagObject,"classid");
			codebase = regras.getAtributo(tagObject,"codebase");
			codetype = regras.getAtributo(tagObject,"codetype");
			codetype = regras.getAtributo(tagObject,"data");
			declare = regras.getAtributo(tagObject,"declare");
			name = regras.getAtributo(tagObject,"name");
			standby =  regras.getAtributo(tagObject,"standby");
			type =  regras.getAtributo(tagObject,"type");
			usemap =  regras.getAtributo(tagObject,"usemap");
			vspace =  regras.getAtributo(tagObject,"vspace");
			
			
			//encontrar os param
			String movie="",quality,bgcolor,wmode,flashVars="playerMode=embedded";
			
			
			Pattern param = Pattern.compile("<param\\s[^>]*>",Pattern.CASE_INSENSITIVE);
			Matcher matParam = param.matcher(tagObject);
			while(matParam.find()){
				String pName = regras.getAtributo(matParam.group(), "name");
				String pValue = regras.getAtributo(matParam.group(), "value");
				if(pName.toLowerCase().equals("movie")){
					movie = pValue;
				}else if(pName.toLowerCase().equals("quality")){
					quality = pValue;
				}else if(pName.toLowerCase().equals("bgcolor")){
					bgcolor = pValue;
				}else if(pName.toLowerCase().equals("wmode")){
					wmode = pValue;
				}else if(pName.toLowerCase().equals("FlashVars")){
					flashVars += "&amp;" + pValue;
				}
			}
			if(movie==null || movie.equals("")) continue;
			//criar a nova tag
			String novaTag = "<object type=\"application/x-shockwave-flash\" data=\""+movie+"\" width=\""+width+"\" height=\""+height+"\" ";
			if(id!=null && !id.equals("")){
				novaTag += "id=\""+id+"\" ";
			}
			if(title!=null && !title.equals("")){
				novaTag += "title=\""+title+"\" ";
			}
			novaTag += ">";
			novaTag+=tagObject.replaceAll("(<object\\s[^>]*>|<embed\\s[^>]*>|</object>|</embed>|<param\\s*name=\"FlashVars\"[^>]*>)","");
			novaTag+="<param name=\"FlashVars\" value=\""+flashVars+"\" />"+"</object>";
			retorno = retorno.replace(tagObject, novaTag);
		}
		return retorno;
	}
	public void setColocarDocType(boolean b) {
		colocarDocType = b;
	}
}
